import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';

let app: INestApplication;

beforeAll(async () => {
  const moduleFixture: TestingModule = await Test.createTestingModule({
    imports: [AppModule],
  }).compile();

  app = moduleFixture.createNestApplication();
  await app.init();
});

afterAll(async () => {
  await app.close();
});

describe('AuthController (e2e)', () => {
  describe('/api/auth/register (POST)', () => {
    it('should register a new user successfully', () => {
      const registerData = {
        email: 'test@example.com',
        password: '123456',
        name: '测试用户',
      };

      return request(app.getHttpServer())
        .post('/api/auth/register')
        .send(registerData)
        .expect(201)
        .expect((res) => {
          expect(res.body).toHaveProperty('user');
          expect(res.body).toHaveProperty('token');
          expect(res.body.user).toHaveProperty('id');
          expect(res.body.user.email).toBe(registerData.email);
          expect(res.body.user.name).toBe(registerData.name);
          expect(res.body.user.role).toBe('USER');
          expect(typeof res.body.token).toBe('string');
        });
    });

    it('should return 409 when user already exists', async () => {
      const registerData = {
        email: 'existing@example.com',
        password: '123456',
        name: '已存在用户',
      };

      // First registration
      await request(app.getHttpServer())
        .post('/api/auth/register')
        .send(registerData)
        .expect(201);

      // Second registration with same email
      return request(app.getHttpServer())
        .post('/api/auth/register')
        .send(registerData)
        .expect(409)
        .expect((res) => {
          expect(res.body.message).toBe('用户已存在');
        });
    });

    it('should return 400 for invalid email', () => {
      const invalidData = {
        email: 'invalid-email',
        password: '123456',
        name: '测试用户',
      };

      return request(app.getHttpServer())
        .post('/api/auth/register')
        .send(invalidData)
        .expect(400);
    });

    it('should return 400 for short password', () => {
      const invalidData = {
        email: 'test@example.com',
        password: '123',
        name: '测试用户',
      };

      return request(app.getHttpServer())
        .post('/api/auth/register')
        .send(invalidData)
        .expect(400);
    });
  });

  describe('/api/auth/login (POST)', () => {
    beforeEach(async () => {
      // Create a test user for login tests
      const registerData = {
        email: 'login-test@example.com',
        password: '123456',
        name: '登录测试用户',
      };

      await request(app.getHttpServer())
        .post('/api/auth/register')
        .send(registerData)
        .expect(201);
    });

    it('should login successfully with correct credentials', () => {
      const loginData = {
        email: 'login-test@example.com',
        password: '123456',
      };

      return request(app.getHttpServer())
        .post('/api/auth/login')
        .send(loginData)
        .expect(200)
        .expect((res) => {
          expect(res.body).toHaveProperty('user');
          expect(res.body).toHaveProperty('token');
          expect(res.body.user.email).toBe(loginData.email);
          expect(typeof res.body.token).toBe('string');
        });
    });

    it('should return 401 for incorrect password', () => {
      const loginData = {
        email: 'login-test@example.com',
        password: 'wrong-password',
      };

      return request(app.getHttpServer())
        .post('/api/auth/login')
        .send(loginData)
        .expect(401)
        .expect((res) => {
          expect(res.body.message).toBe('邮箱或密码错误');
        });
    });

    it('should return 401 for non-existent user', () => {
      const loginData = {
        email: 'nonexistent@example.com',
        password: '123456',
      };

      return request(app.getHttpServer())
        .post('/api/auth/login')
        .send(loginData)
        .expect(401)
        .expect((res) => {
          expect(res.body.message).toBe('邮箱或密码错误');
        });
    });

    it('should return 400 for invalid email format', () => {
      const loginData = {
        email: 'invalid-email',
        password: '123456',
      };

      return request(app.getHttpServer())
        .post('/api/auth/login')
        .send(loginData)
        .expect(400);
    });
  });
});

describe('User Role & Permission (e2e)', () => {
  let adminToken: string;
  let userToken: string;
  let authorToken: string;
  let userId: string;
  let authorId: string;

  beforeAll(async () => {
    // 注册管理员
    const adminRes = await request(app.getHttpServer())
      .post('/api/auth/register')
      .send({ email: 'admin@example.com', password: '123456', name: '管理员' });
    adminToken = adminRes.body.token;
    userId = adminRes.body.user.id;

    // 手动将其角色改为 ADMIN
    await request(app.getHttpServer())
      .put('/api/users/role')
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ userId, role: 'ADMIN' });

    // 注册普通用户
    const userRes = await request(app.getHttpServer())
      .post('/api/auth/register')
      .send({ email: 'user@example.com', password: '123456', name: '普通用户' });
    userToken = userRes.body.token;

    // 注册作者
    const authorRes = await request(app.getHttpServer())
      .post('/api/auth/register')
      .send({ email: 'author@example.com', password: '123456', name: '作者' });
    authorToken = authorRes.body.token;
    authorId = authorRes.body.user.id;

    // 手动将其角色改为 AUTHOR
    await request(app.getHttpServer())
      .put('/api/users/role')
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ userId: authorId, role: 'AUTHOR' });
  });

  it('ADMIN 可以访问 /api/users/profile', async () => {
    const res = await request(app.getHttpServer())
      .get('/api/users/profile')
      .set('Authorization', `Bearer ${adminToken}`)
      .expect(200);
    expect(res.body.role).toBe('ADMIN');
  });

  it('USER 可以访问 /api/users/profile', async () => {
    const res = await request(app.getHttpServer())
      .get('/api/users/profile')
      .set('Authorization', `Bearer ${userToken}`)
      .expect(200);
    expect(res.body.role).toBe('USER');
  });

  it('USER 不能访问 /api/users/role', async () => {
    await request(app.getHttpServer())
      .put('/api/users/role')
      .set('Authorization', `Bearer ${userToken}`)
      .send({ userId, role: 'ADMIN' })
      .expect(403);
  });

  it('ADMIN 可以修改用户角色', async () => {
    await request(app.getHttpServer())
      .put('/api/users/role')
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ userId, role: 'USER' })
      .expect(200);
  });

  it('AUTHOR 可以访问 /api/users/profile', async () => {
    const res = await request(app.getHttpServer())
      .get('/api/users/profile')
      .set('Authorization', `Bearer ${authorToken}`)
      .expect(200);
    expect(res.body.role).toBe('AUTHOR');
  });
});

describe('PostController 权限 (e2e)', () => {
  let adminToken: string;
  let userToken: string;
  let authorToken: string;
  let postId: string;

  beforeAll(async () => {
    // 注册并设置角色
    const adminRes = await request(app.getHttpServer())
      .post('/api/auth/register')
      .send({ email: 'postadmin@example.com', password: '123456', name: '文章管理员' });
    adminToken = adminRes.body.token;
    const adminId = adminRes.body.user.id;
    await request(app.getHttpServer())
      .put('/api/users/role')
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ userId: adminId, role: 'ADMIN' });

    const authorRes = await request(app.getHttpServer())
      .post('/api/auth/register')
      .send({ email: 'postauthor@example.com', password: '123456', name: '文章作者' });
    authorToken = authorRes.body.token;
    const authorId = authorRes.body.user.id;
    await request(app.getHttpServer())
      .put('/api/users/role')
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ userId: authorId, role: 'AUTHOR' });

    const userRes = await request(app.getHttpServer())
      .post('/api/auth/register')
      .send({ email: 'postuser@example.com', password: '123456', name: '文章普通用户' });
    userToken = userRes.body.token;
  });

  it('USER 不能创建文章', async () => {
    await request(app.getHttpServer())
      .post('/api/posts')
      .set('Authorization', `Bearer ${userToken}`)
      .send({ title: 'test', content: 'test' })
      .expect(403);
  });

  it('AUTHOR 可以创建文章', async () => {
    const res = await request(app.getHttpServer())
      .post('/api/posts')
      .set('Authorization', `Bearer ${authorToken}`)
      .send({ title: '作者文章', content: '内容' })
      .expect(201);
    postId = res.body.id || 'mock-id';
  });

  it('USER 不能编辑文章', async () => {
    await request(app.getHttpServer())
      .put(`/api/posts/${postId}`)
      .set('Authorization', `Bearer ${userToken}`)
      .send({ title: '新标题' })
      .expect(403);
  });

  it('AUTHOR 可以编辑文章', async () => {
    await request(app.getHttpServer())
      .put(`/api/posts/${postId}`)
      .set('Authorization', `Bearer ${authorToken}`)
      .send({ title: '新标题' })
      .expect(200);
  });

  it('USER 不能删除文章', async () => {
    await request(app.getHttpServer())
      .delete(`/api/posts/${postId}`)
      .set('Authorization', `Bearer ${userToken}`)
      .expect(403);
  });

  it('AUTHOR 不能删除文章', async () => {
    await request(app.getHttpServer())
      .delete(`/api/posts/${postId}`)
      .set('Authorization', `Bearer ${authorToken}`)
      .expect(403);
  });

  it('ADMIN 可以删除文章', async () => {
    await request(app.getHttpServer())
      .delete(`/api/posts/${postId}`)
      .set('Authorization', `Bearer ${adminToken}`)
      .expect(200);
  });

  it('所有角色都能获取已发布文章列表', async () => {
    await request(app.getHttpServer())
      .get('/api/posts')
      .set('Authorization', `Bearer ${userToken}`)
      .expect(200);
    await request(app.getHttpServer())
      .get('/api/posts')
      .set('Authorization', `Bearer ${authorToken}`)
      .expect(200);
    await request(app.getHttpServer())
      .get('/api/posts')
      .set('Authorization', `Bearer ${adminToken}`)
      .expect(200);
  });
});
